package pl.edu.pb.wi.pwnography.parsing;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;

import org.springframework.web.multipart.commons.CommonsMultipartFile;

import pl.edu.pb.wi.pwnography.model.ParsedData;
import pl.edu.pb.wi.pwnography.model.form.FileUploadForm;

public class TextFileParser {
    private static final Logger log = Logger.getLogger(TextFileParser.class
	    .getName());
    public static final String COLUMN_NAME = "Col";
    private static final char COMMENT_TAG = '#';
    List<String> rawData;
    private String fileName;

    public TextFileParser(FileUploadForm file) throws IOException {
	CommonsMultipartFile cmpf = file.getFile();
	InputStream is = cmpf.getInputStream();
	InputStreamReader isr = new InputStreamReader(is);
	BufferedReader br = new BufferedReader(isr);
	fileName = file.getFile().getOriginalFilename();

	rawData = initialParse(br);
    }

    public TextFileParser(CommonsMultipartFile multipartFile)
	    throws IOException {
	InputStream is = multipartFile.getInputStream();
	InputStreamReader isr = new InputStreamReader(is);
	BufferedReader br = new BufferedReader(isr);
	fileName = multipartFile.getOriginalFilename();

	rawData = initialParse(br);
    }

    public String getFileName() {
	return fileName;
    }

    /*
     * Parse stream into String lines and remove all commented lines.
     */
    private List<String> initialParse(BufferedReader br) throws IOException {
	List<String> tempLines = new ArrayList<String>();

	String line;
	while ((line = br.readLine()) != null) {
	    line = line.trim();

	    if (line.length() > 0 && line.charAt(0) != COMMENT_TAG) {
		tempLines.add(line);
	    } else {
		log.info(String.format("Comment line: %s", line));
	    }
	}

	log.info(String.format("File successfully read. Number of lines: %d",
		tempLines.size()));
	return tempLines;
    }

    public ParsedData parseData(boolean haveColumnNames) throws Exception {
	if (rawData.size() < 1) {
	    throw new Exception(
		    "File is empty or contains only commented lines");
	}

	LinkedList<String> tempList = new LinkedList<String>(rawData);

	String firstRow = tempList.getFirst();
	String[] columnNames = firstRow.split("\\s+");
	if (haveColumnNames) {
	    tempList.remove(firstRow);
	} else {
	    for (int i = 0; i < columnNames.length; ++i)
		columnNames[i] = String.format("%s%d", COLUMN_NAME, i + 1);
	}
	log.info(String.format("Column names: %s Number of columns: %d",
		firstRow, columnNames.length));

	Map<String, List<Object>> data = new HashMap<String, List<Object>>();
	for (String row : tempList) {
	    String[] values = row.split("\\s+");
	    if (values.length != columnNames.length) {
		throw new Exception(
			String.format(
				"Number of columns and number of values in row doesn't match. Number of columns: %d Number od values in row: %d \nRow: %s",
				columnNames.length, values.length, row));
	    }

	    for (int i = 0; i < values.length; ++i) {
		List<Object> columnValues = data.get(columnNames[i]);
		if (columnValues == null) {
		    columnValues = new ArrayList<Object>();
		    data.put(columnNames[i], columnValues);
		}

		String trimmedValue = values[i].trim().replace(',', '.');
		try {
		    Float floatValue = Float.parseFloat(trimmedValue);
		    columnValues.add(floatValue);
		} catch (NumberFormatException ex) {
		    columnValues.add(trimmedValue);
		}
	    }
	}

	// Map<String, ParsedData.COLUMN_DATA_TYPE> dataTypes = new
	// LinkedHashMap<String, ParsedData.COLUMN_DATA_TYPE>();
	int rowCount = 0;
	for (String column : columnNames) {
	    int floatAmount = 0;
	    int stringAmount = 0;

	    for (Object o : data.get(column)) {
		if (o instanceof Float) {
		    ++floatAmount;
		} else {
		    ++stringAmount;
		}
	    }

	    if (floatAmount > 0 && stringAmount > 0) {
		throw new Exception(String.format(
			"Column [%s] contains (%d)floats and (%d)string",
			column, floatAmount, stringAmount));
	    } else {
		rowCount = floatAmount > 0 ? floatAmount : stringAmount;
	    }
	    // if (floatAmount > 0 && stringAmount == 0) {
	    // dataTypes.put(column, ParsedData.COLUMN_DATA_TYPE.FLOAT);
	    // }
	    // if (stringAmount > 0 && floatAmount == 0) {
	    // dataTypes.put(column, ParsedData.COLUMN_DATA_TYPE.STRING);
	    // }
	}

	return new ParsedData(data, fileName, rowCount);
    }
}
